home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / wvnsrc75.zip / WVLIST.C < prev    next >
C/C++ Source or Header  |  1992-11-06  |  18KB  |  673 lines

  1. /*-- WVLIST.C -- File containing functions to deal with the NNTP LIST
  2.  *   command, which lists all the newsgroups and their status.
  3.  *
  4.  *   Mark Riordan   25 October 1990
  5.  */
  6.  
  7. #include "windows.h"
  8. #include "wvglob.h"
  9. #include "winvn.h"
  10. #ifndef MAC
  11. #include "winundoc.h"
  12. #include <ctype.h>
  13. #endif
  14.  
  15.  
  16. #define HASHMAX 9721
  17. #define MAXGROUPS  3200
  18.  
  19. TypLine far *far * NetHashTable;
  20. HANDLE hNetHashTable;
  21.  
  22. HANDLE htohNewGroupLines;
  23. HANDLE far *lphNewGroupLines;    /* array of handles to new group lines */
  24.  
  25.  
  26. HWND hDlgList;            /* Handle to child list box window. */
  27.  
  28. #define MyGlobalUnlock(hWhat)  MRRGlobalUnlock(hWhat,__LINE__)
  29.  
  30. void MRRGlobalUnlock (HANDLE hWhat, WORD wLine);
  31.  
  32. void
  33. MRRGlobalUnlock (hWhat, wLine)
  34.      HANDLE hWhat;
  35.      WORD wLine;
  36. {
  37.   WORD LockCount;
  38.   char mybuf[80];
  39.  
  40.   if (!(LockCount = (GMEM_LOCKCOUNT & GlobalFlags (hWhat))))
  41.     {
  42. #if 0
  43.       sprintf (mybuf, "In WVLIST.C line %d", wLine);
  44.       MessageBox (hWndConf, mybuf, "Attempt to unlock 0 LockCount", MB_OK);
  45. #endif
  46.     }
  47.   else
  48.     {
  49.       GlobalUnlock (hWhat);
  50.     }
  51.  
  52. }
  53.  
  54. /*--- function StartList -----------------------------------------------
  55.  *
  56.  *  Initiate the process of sending a LIST command and using its
  57.  *  output to update our list of news groups.
  58.  */
  59.  
  60. void
  61. StartList ()
  62. {
  63.   unsigned int hashval;
  64.  
  65.   CommState = ST_LIST_RESP;
  66.   CommBusy = TRUE;
  67.   PutCommLine ("LIST", 4);
  68.   Initializing = INIT_SCANNING_NETDOC;
  69.   InvalidateRect (hWndConf, NULL, FALSE);
  70.   SendMessage (hWndConf, WM_PAINT, 0, 0L);
  71.  
  72.   /* Set up hash table for group names */
  73.  
  74.   hNetHashTable = GlobalAlloc (GMEM_MOVEABLE, (long) HASHMAX * sizeof (TypLine far *));
  75.   NetHashTable = (TypLine far * far *) GlobalLock (hNetHashTable);
  76.   for (hashval = 0; hashval < HASHMAX; hashval++)
  77.     {
  78.       NetHashTable[hashval] = (TypLine far *) 0;
  79.     }
  80.  
  81.   HashNetGroups (&NetDoc, NetHashTable);
  82.   InvalidateRect (hWndConf, NULL, FALSE);
  83.   Initializing = INIT_GETTING_LIST;
  84.   SendMessage (hWndConf, WM_PAINT, 0, 0L);
  85.  
  86.   /* Set up table of pointers to new group lines.
  87.    */
  88. #if 0
  89.   htohNewGroupLines = GlobalAlloc (GMEM_MOVEABLE, (long) MAXGROUPS * sizeof (HANDLE));
  90.   lphNewGroupLines = (HANDLE far *) GlobalLock (htohNewGroupLines);
  91. #endif
  92.  
  93.   InitGroupTable ();
  94. }
  95.  
  96. /*--- function InitGroupTable -------------------------------------------
  97.  *
  98.  *  Allocate the NewGroupTable.
  99.  *
  100.  *  Exit:   hNewGroupTable    is the handle
  101.  *          NewGroupTable     points to the table
  102.  *          nNewGroups        has been initialized to 0.
  103.  */
  104. void
  105. InitGroupTable (void)
  106. {
  107.   hNewGroupTable = GlobalAlloc (GMEM_MOVEABLE, (long) MAXGROUPS * sizeof (TypLine far *));
  108.   NewGroupTable = (void far * far *) GlobalLock (hNewGroupTable);
  109.  
  110.   nNewGroups = 0;
  111. }
  112.  
  113.  
  114. /*--- function HashNetGroups ------------------------------------------
  115.  *
  116.  *  Enter all the groups in the Net document into the hash table.
  117.  *
  118.  *  Exit    All blocks in the document are locked.
  119.  *          HashTable   is (partially) filled with pointers to
  120.  *                      each of the lines in "Doc".
  121.  */
  122.  
  123. void
  124. HashNetGroups (Doc, HashTable)
  125.      TypDoc *Doc;
  126.      TypLine far *far * HashTable;
  127. {
  128.   TypBlock far *BlockPtr;
  129.   TypLine far *LinePtr;
  130.   TypLine far *far * hashptr;
  131.   unsigned int hashval;
  132.   HANDLE hBlock;
  133.   unsigned int Offset;
  134.   unsigned int TextOffset;
  135.   unsigned char far *textptr;
  136.   TypLineID MyLineID;
  137.  
  138.   /* Lock all blocks in the document */
  139.   hBlock = Doc->hFirstBlock;
  140.   do
  141.     {
  142.       BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  143.       hBlock = BlockPtr->hNextBlock;
  144.     }
  145.   while (hBlock);
  146.  
  147.  
  148.   /* Now start at the beginning of the document, going through
  149.    * each line in the document, hashing its group name into the table.
  150.    */
  151.  
  152.   hBlock = Doc->hFirstBlock;
  153.   Offset = sizeof (TypBlock);
  154.   MyLineID = 0L;
  155.  
  156.   LockLine (hBlock, Offset, MyLineID, &BlockPtr, &LinePtr);
  157.  
  158.   TextOffset = Doc->OffsetToText;
  159.  
  160.   if (LinePtr->length != END_OF_BLOCK)
  161.     {
  162.       do
  163.     {
  164.       textptr = ((unsigned char far *) LinePtr) + TextOffset;
  165.       hashval = HashGroup (textptr);
  166.       while (HashTable[hashval])
  167.         {
  168.           hashval = (hashval + 1) % HASHMAX;
  169.         }
  170.       HashTable[hashval] = LinePtr;
  171.     }
  172.       while (NextLine (&BlockPtr, &LinePtr));
  173.     }
  174.   UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  175. }
  176.  
  177.  
  178. /*--- function ProcListLine ---------------------------------------------
  179.  *
  180.  *  Process a line received from the NNTP LIST command output.
  181.  *  Each line from the LIST command has the form:
  182.  *   <groupname> <highest_art_#> <lowest_art_#> {y|n|m}
  183.  */
  184.  
  185. void
  186. ProcListLine (ListLine)
  187.      unsigned char *ListLine;
  188. {
  189.   unsigned int hashval;
  190.   char far *textptr;
  191.   unsigned char *cptr, *restline;
  192.   char mygroupline[BLOCK_SIZE];
  193.   TypGroup *mygroup;
  194.   TypGroup far *netgroup;
  195.   long int ArtNum;
  196.  
  197.   if ((++RcvLineCount) % UPDATE_TITLE_FREQ == 0)
  198.     {
  199.       InvalidateRect (hWndConf, NULL, FALSE);
  200.       if (RcvLineCount % (UPDATE_TITLE_FREQ * 25) == 0)
  201.     {
  202.       UpdateWindow (hWndConf);
  203.     }
  204.     }
  205.  
  206.   /* Replace the first blank in the input line with a zero. */
  207.   for (cptr = ListLine; *cptr && *cptr != ' '; cptr++);
  208.   *cptr = '\0';
  209.   restline = cptr + 1;        /* points to highest art # */
  210.  
  211.   hashval = HashGroup (ListLine);
  212. checkhash:;
  213.   if (!NetHashTable[hashval])
  214.     {
  215.       /* This is a new group.
  216.        * Create a Group line from the information in this line.
  217.        */
  218.       CrackGroupLine (ListLine, (TypLine *) (mygroupline));
  219.       mygroup = (TypGroup *) (mygroupline + sizeof (TypLine));
  220.       GetNum (&restline, &(mygroup->ServerLast));
  221.       GetNum (&restline, &(mygroup->ServerFirst));
  222.       mygroup->HighestPrevSeen = 0;
  223.       mygroup->nRanges = 0;
  224.  
  225.       AddGroupToTable (mygroupline);
  226. #if 0
  227.       MessageBox (hWndConf, ListLine, "New Group:", MB_OK);
  228. #endif
  229.     }
  230.   else
  231.     {
  232.       textptr = ((char far *) NetHashTable[hashval] + sizeof (TypLine) + sizeof (TypGroup));
  233.       if (lstrcmp (textptr, ListLine))
  234.     {
  235.       hashval = (hashval + 1) % HASHMAX;
  236.       goto checkhash;
  237.     }
  238.       else
  239.     {
  240.       /* This group is already present in NetDoc.
  241.            * Update the ServerFirst and ServerLast fields.
  242.            */
  243.       netgroup = (TypGroup far *) ((char far *) NetHashTable[hashval] + sizeof (TypLine));
  244.       GetNum (&restline, &ArtNum);
  245.       netgroup->ServerLast = ArtNum;
  246.       GetNum (&restline, &ArtNum);
  247.       netgroup->ServerFirst = ArtNum;
  248.       netgroup->ServerEstNum = netgroup->ServerLast - netgroup->ServerFirst + 1;
  249.     }
  250.     }
  251.  
  252. }
  253.  
  254. /*--- function AddGroupToTable ----------------------------------------
  255.  *
  256.  *  Add a group line, formatted for eventual inclusion in NetDoc,
  257.  *  to NewGroupTable.
  258.  *
  259.  *  Entry:  GroupLine      is the line to add to the table.
  260.  *
  261.  *  Exit:   NewGroupTable  contains the line
  262.  *          nNewGroups     has been incremented.
  263.  */
  264. void
  265. AddGroupToTable (char far * GroupLine)
  266. {
  267.   HANDLE hLine;
  268.   char far *AllocPtr;
  269.   int mylength;
  270.  
  271.   /* Create a copy of this line in far memory and place pointers
  272.    * to in our arrays.
  273.    */
  274.  
  275.   mylength = ((TypLine far *) GroupLine)->length + sizeof (HANDLE);
  276.   hLine = GlobalAlloc (GMEM_MOVEABLE, (long) mylength);
  277.   AllocPtr = (char far *) GlobalLock (hLine);
  278.   MoveBytes ((char far *) &hLine, AllocPtr, sizeof (hLine));
  279.   MoveBytes ((char far *) GroupLine, AllocPtr + sizeof (hLine), mylength - sizeof (hLine));
  280.  
  281.   NewGroupTable[nNewGroups] = AllocPtr;
  282.   nNewGroups++;
  283. }
  284.  
  285. /*--- function HashGroup -------------------------------------------------
  286.  *
  287.  *  Hash a string into an unsigned integer.
  288.  *
  289.  *  This hash function is designed based on information from a
  290.  *  handout for a UC Berkeley class CS 60C, Spring 1990, Clancy/
  291.  *  Harrison.  I picked up the handout while wandering around on
  292.  *  Berkeley's campus in May 1990.  The handout in turn is based
  293.  *  on the McKenzie, et al., article "Selecting a Hashing Algorithm"
  294.  *  in Software Practice and Experience, Vol 20, no 2, Feb 1990.
  295.  *  The algorithm is similar to that used in the AT&T C++ compiler.
  296.  *  /mrr
  297.  */
  298.  
  299. unsigned int
  300. HashGroup (gname)
  301.      unsigned char far *gname;
  302. {
  303.   long unsigned int sum = 0;
  304.   unsigned int hash;
  305.  
  306.   for (; *gname; gname++)
  307.     {
  308.       sum = (sum << 1) + *gname;
  309.     }
  310.   hash = sum % HASHMAX;
  311.   return (hash);
  312. }
  313.  
  314.  
  315. /*--- function ProcEndList -------------------------------------------
  316.  *
  317.  *  Do the final processing when we have reached the end of the
  318.  *  list of newsgroups sent us via the LIST command.
  319.  */
  320.  
  321. void
  322. ProcEndList ()
  323. {
  324.   char mybuf[80];
  325.   char far *cptr;
  326.   int j, selstate;
  327.   FARPROC lpfnWinVnGroupListDlg;
  328.   TypBlock far *BlockPtr;
  329.   TypLine far *LinePtr;
  330.   TypGroup far *group;
  331.   WORD LockCount;
  332.  
  333. #if 0
  334.   sprintf (mybuf, "%d new groups found.", nNewGroups);
  335.   MessageBox (hWndConf, mybuf, "", MB_OK);
  336. #endif
  337.  
  338.   ShellSort (NewGroupTable, nNewGroups, sizeof (void far *), GroupCompare);
  339.  
  340. #if 0
  341.   for (j = 0; j < nNewGroups; j++)
  342.     {
  343.       cptr = ((char far *) NewGroupTable[j]) + sizeof (HANDLE) + sizeof (TypLine) + sizeof (TypGroup);
  344.       MessageBox (hWndConf, cptr, "New Group:", MB_OK);
  345.     }
  346. #endif
  347.   lpfnWinVnGroupListDlg = MakeProcInstance (WinVnGroupListDlg, hInst);
  348.  
  349.   /* Display dialog box of new groups.
  350.    */
  351.   if (nNewGroups && DialogBox (hInst, "WinVnGroupList", hWndConf, lpfnWinVnGroupListDlg))
  352.     {
  353.       /* The user has clicked OK, so add all the new groups to the
  354.        * Net document.  Subscribed groups go at the end of the Subscribed
  355.        * section at the top of the doc.
  356.        * Unsubscribed groups go in the section below, in alphabetical order.
  357.        */
  358.       MergeGroups (ADD_SUBSCRIBED_END_OF_SUB);
  359.     }
  360.  
  361.   /* Unlock and/or free memory in NetDoc and NewGroupTable.  */
  362.  
  363.   CleanUpGroupTable ();
  364.  
  365.   /* Unlock and free the hash table.   */
  366.  
  367.   LockCount = GMEM_LOCKCOUNT & GlobalFlags (hNetHashTable);
  368.   MyGlobalUnlock (hNetHashTable);
  369.   GlobalFree (hNetHashTable);
  370.  
  371.   InvalidateRect (hWndConf, NULL, FALSE);
  372.   SetNetDocTitle ();
  373. }
  374.  
  375. /*--- function GroupCompare --------------------------------------------
  376.  *
  377.  *  Compare two group lines alphabetically by group name.
  378.  */
  379. int
  380. GroupCompare (g1, g2)
  381.      TypLine const far *far * g1;
  382.      TypLine const far *far * g2;
  383. {
  384.   char far *gch1, far * gch2;
  385.  
  386.   gch1 = (char far *) *g1 + sizeof (HANDLE) + sizeof (TypLine) + sizeof (TypGroup);
  387.   gch2 = (char far *) *g2 + sizeof (HANDLE) + sizeof (TypLine) + sizeof (TypGroup);
  388.  
  389.   return (lstrcmp (gch1, gch2));
  390. }
  391.  
  392.  
  393. /*-- function WinVnGroupListDlg ---------------------------------------
  394.  *
  395.  *  Dialog function to handle selection of new newsgroups to
  396.  *  subscribe to.  (I know, don't end a sentence with "to".)
  397.  */
  398.  
  399. BOOL FAR PASCAL 
  400. WinVnGroupListDlg (hDlg, iMessage, wParam, lParam)
  401.      HWND hDlg;
  402.      unsigned iMessage;
  403.      WORD wParam;
  404.      LONG lParam;
  405. {
  406.   int j, selstate;
  407.   char far *cptr;
  408.   WORD notification;
  409.   int wItem;
  410.   void far *AllocPtr;
  411.   TypGroup far *group;
  412.  
  413.  
  414.   switch (iMessage)
  415.     {
  416.  
  417.     case WM_INITDIALOG:
  418.       hDlgList = GetDlgItem (hDlg, IDD_GROUP_LISTBOX);
  419.  
  420.       SendMessage (hDlgList, WM_SETREDRAW, FALSE, 0L);
  421.       for (j = 0; j < nNewGroups; j++)
  422.     {
  423.       cptr = 0;
  424.       cptr = (NewGroupTable[j]);
  425.       cptr += sizeof (HANDLE) + sizeof (TypLine) + sizeof (TypGroup);
  426. /* Petzold misdocuments this??? Win3.1 kernel complains about this -1 */
  427. /*       SendMessage (hDlgList, LB_ADDSTRING, -1, (LONG) cptr); */
  428.        SendMessage (hDlgList, LB_ADDSTRING, 0, (LONG) cptr);
  429.     }
  430.       SendMessage (hDlgList, WM_SETREDRAW, TRUE, 0L);
  431.       return TRUE;
  432.       break;
  433.  
  434.     case WM_COMMAND:
  435.       switch (wParam)
  436.     {
  437.     case IDOK:
  438.       EndDialog (hDlg, TRUE);
  439.       break;
  440.  
  441.     case IDCANCEL:
  442.       EndDialog (hDlg, FALSE);
  443.       break;
  444. #if 0
  445.     case IDD_GROUP_LISTBOX:
  446.       notification = HIWORD (lParam);
  447.       if (notification == LBN_SELCHANGE)
  448.         {
  449.           wItem = (WORD) SendMessage (hDlgList, LB_GETCURSEL, 0, 0L);
  450.  
  451.         }
  452.       break;
  453. #endif
  454.     default:
  455.       return FALSE;
  456.     }
  457.       break;
  458.  
  459.     case WM_DESTROY:
  460.       /* Mark the groups pointed to by NewGroupTable as Subscribed
  461.        * or not, depending upon whether they are now selected.
  462.        */
  463.       for (j = 0; j < nNewGroups; j++)
  464.     {
  465.       selstate = (WORD) SendMessage (hDlgList, LB_GETSEL, j, 0L);
  466.       AllocPtr = (char far *) NewGroupTable[j] + sizeof (HANDLE);
  467.       group = (TypGroup far *) ((char far *) AllocPtr + sizeof (TypLine));
  468.       group->Subscribed = selstate;
  469.     }
  470.       break;
  471.  
  472.     default:
  473.       return FALSE;
  474.       break;
  475.     }
  476.   return TRUE;
  477. }
  478.  
  479. /*--- function PositionEndSubscribed ----------------------------------
  480.  *
  481.  *  Position a pointer to the end of the subscribed section at the
  482.  *  beginning of the net document.
  483.  *
  484.  *  Entry   None
  485.  *
  486.  *  Exit    BlockPtr and LinePtr  point to the place in NetDoc just
  487.  *            beyond the last subscribed group.  We assume that
  488.  *            all subscribed groups go at the beginning of the
  489.  *            document.
  490.  */
  491. void
  492. PositionEndSubscribed (TypBlock far ** BlockPtr, TypLine far ** LinePtr)
  493. {
  494.   BOOL advance;
  495.   TypGroup far *group;
  496.  
  497.   TopOfDoc (&NetDoc, BlockPtr, LinePtr);
  498.   advance = TRUE;
  499.   do
  500.     {
  501.       group = (TypGroup far *) ((char far *) *LinePtr + sizeof (TypLine));
  502.       if (group->Subscribed)
  503.     {
  504.       advance = NextLine (BlockPtr, LinePtr);
  505.     }
  506.       else
  507.     {
  508.       advance = FALSE;
  509.     }
  510.     }
  511.   while (advance);
  512. }
  513.  
  514. /*--- function MergeGroups ----------------------------------------
  515.  *
  516.  *  Merge a list of groups into NetDoc.
  517.  *
  518.  *  Entry:  NewGroupTable  is an array of pointers to TypGroup structures
  519.  *                         of groups to be merged into NetDoc.
  520.  *          hNewGroupTable is the handle to the above.
  521.  *          nNewGroups     is the number of groups in the table.
  522.  *          WhereSubscribed   indicates where new subscribed groups
  523.  *                         should be added.
  524.  *                         ADD_SUBSCRIBED_END_OF_SUB indicates that
  525.  *                           they should be added at the end of the subscribed
  526.  *                           list, before the unsubscribed groups.
  527.  *                         ADD_SUBSCRIBED_TOP_OF_DOC indicates that they
  528.  *                           should be added at the top of the document.
  529.  *  Exit:   The groups in the table have been added to NetDoc, and
  530.  *            the entries in GroupTable have been freed from memory.
  531.  *            Also, GroupTable itself has been freed.
  532.  */
  533. void
  534. MergeGroups (int WhereSubscribed)
  535. {
  536.   TypBlock far *BlockPtr;
  537.   TypLine far *LinePtr;
  538.   TypGroup far *group;
  539.   char far *netcptr;
  540.   char far *grpcptr;
  541.   void far *AllocPtr;
  542.   HANDLE hLine;
  543.   unsigned int Offset;
  544.   TypLineID MyLineID;
  545.   char myline[BLOCK_SIZE];
  546.   int j, advance, at_end = 0;
  547.  
  548.   switch (WhereSubscribed)
  549.     {
  550.     case ADD_SUBSCRIBED_END_OF_SUB:
  551.       PositionEndSubscribed (&BlockPtr, &LinePtr);
  552.       break;
  553.     case ADD_SUBSCRIBED_TOP_OF_DOC:
  554.       TopOfDoc (&NetDoc, &BlockPtr, &LinePtr);
  555.       break;
  556.     }
  557.  
  558.   /* BlockPtr and LinePtr point to the
  559.    * place to add new subscribed groups.
  560.    * Loop through the new groups; for subscribed groups, add
  561.    * them to NetDoc at this point.
  562.    * For each subscribed group, unlock and free the corresponding
  563.    * line pointed to by NewGroupTable.  Set the table entry
  564.    * to 0 to indicate that this group has been dealt with.
  565.    */
  566.  
  567.   for (j = 0; j < nNewGroups; j++)
  568.     {
  569.       AllocPtr = (char far *) NewGroupTable[j] + sizeof (HANDLE);
  570.       group = (TypGroup far *) ((char far *) AllocPtr + sizeof (TypLine));
  571.       if (group->Subscribed)
  572.     {
  573.       /* This group has been selected and should be subscribed to.
  574.            */
  575.       MoveBytes (AllocPtr, myline, ((TypLine far *) AllocPtr)->length);
  576.       AddLine ((TypLine *) myline, &BlockPtr, &LinePtr);
  577.       NetDoc.ActiveLines++;
  578.       hLine = *((HANDLE far *) NewGroupTable[j]);
  579.       MyGlobalUnlock (hLine);
  580.       GlobalFree (hLine);
  581.       NewGroupTable[j] = (void far *) 0;
  582.     }
  583.     }
  584.  
  585.   PositionEndSubscribed (&BlockPtr, &LinePtr);
  586.  
  587.   /* Now take a second pass through NewGroupTable, for the
  588.    * unsubscribed groups.  If NewGroupTable[j] is non-zero, then
  589.    * that group should be entered into the second, unsubscribed
  590.    * section of NetDoc, merged in in alphabetical order.
  591.    *
  592.    * BlockPtr and LinePtr point to the first unsubscribed group.
  593.    */
  594.  
  595.   for (j = 0; j < nNewGroups; j++)
  596.     {
  597.       if (NewGroupTable[j])
  598.     {
  599.       /* Search for the right place to add this line. */
  600.  
  601.       AllocPtr = (char far *) NewGroupTable[j] + sizeof (HANDLE);
  602.       grpcptr = ((char far *) AllocPtr) + sizeof (TypLine) + sizeof (TypGroup);
  603.       advance = TRUE;
  604.  
  605.       if (!at_end) {
  606.        do
  607.         {
  608.           netcptr = ((char far *) LinePtr) + sizeof (TypLine) + sizeof (TypGroup);
  609.           if (lstrcmp (grpcptr, netcptr) < 0)
  610.         {
  611.           advance = FALSE;
  612.         }
  613.           else
  614.         {
  615.           advance = NextLine (&BlockPtr, &LinePtr);
  616.           if (!advance) at_end = 1; /* possible bug, getting bad netcptr (smr) */
  617.         }
  618.         }
  619.       while (advance);
  620.       }
  621.  
  622.       /* Now add the new group at this point */
  623.       MoveBytes (AllocPtr, myline, ((TypLine far *) AllocPtr)->length);
  624.       AddLine ((TypLine *) myline, &BlockPtr, &LinePtr);
  625.  
  626.       /* Unlock and free this entry in NewGroupTable. */
  627.       hLine = *((HANDLE far *) NewGroupTable[j]);
  628.       MyGlobalUnlock (hLine);
  629.       GlobalFree (hLine);
  630.     }
  631.     }
  632.  
  633.   UnlockLine (BlockPtr, LinePtr, &hLine, &Offset, &MyLineID);
  634. }
  635.  
  636. /*--- function CleanUpGroupTable ------------------------------------
  637.  *
  638.  *  Clean up after doing processing to add or move groups in NetDoc.
  639.  */
  640. void
  641. CleanUpGroupTable ()
  642. {
  643.   HANDLE hBlock, hBlockNext;
  644.   TypBlock far *BlockPtr;
  645.  
  646.   /* Unlock all blocks in the NetDoc document.
  647.    */
  648.  
  649.   hBlock = NetDoc.hFirstBlock;
  650.   do
  651.     {
  652.       BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  653.       hBlockNext = BlockPtr->hNextBlock;
  654.  
  655.       MyGlobalUnlock (hBlock);
  656. #if 0
  657.       LockCount = GMEM_LOCKCOUNT & GlobalFlags (hBlock);
  658. #endif
  659.       MyGlobalUnlock (hBlock);
  660.       hBlock = hBlockNext;
  661.     }
  662.   while (hBlock);
  663.  
  664.   /* Unlock and free the NewGroupTable itself.  */
  665.  
  666. #if 0
  667.   LockCount = GMEM_LOCKCOUNT & GlobalFlags (hNewGroupTable);
  668. #endif
  669.   MyGlobalUnlock (hNewGroupTable);
  670.   GlobalFree (hNewGroupTable);
  671.  
  672. }
  673.